﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using GameDataTempshitEditor.Widgets;
using System.IO;

namespace GameDataTempshitEditor
{
    public partial class Form1 : Form
    {
        // editor environment
        private Red.GameData.EditorEnvironment _Env;

        // UI
        private DataEditors.DataEditors _DataEditors;
        private GameDataTempshitEditor.Widgets.ClassTree _ClassTree;

        public Form1()
        {
            InitializeComponent();

            // create class tree
            _ClassTree = new ClassTree();
            _ClassTree.OnClassActivated += classTree_OnClassActivated;
            _ClassTree.OnClassCreateNew += _classTree_OnClassCreateNew;
            _ClassTree.Dock = DockStyle.Fill;
            tabPage1.Controls.Add(_ClassTree);

            RefreshUndoRedo();
        }

        void _classTree_OnClassCreateNew(object sender, ClassTreedEventArgs e)
        {
            if (e.Class != null)
            {
                var dataTree = OpenDataObjectTree(e.Class);
                if (dataTree != null)
                {
                    dataTree.CreateNewRootObject(e.Class);
                }
            }
        }

        void dataTree_OnObjectActivated(object sender, Widgets.DataObjectEventArgs e)
        {
            _DataEditors.OpenEditor(e.ObjectView);
        }

        void classTree_OnClassActivated(object sender, Widgets.ClassTreedEventArgs e)
        {
            OpenDataObjectTree( e.Class );
        }

        private void settingsChangeSettings_Click(object sender, EventArgs e)
        {
            ChangeSettings();
        }

        private void Form1_Shown(object sender, EventArgs e)
        {
            // select the reflection data path
            var required = (Properties.Settings.Default.ReflectionPath == "" || Properties.Settings.Default.DataPath == "");
            if (required || !Properties.Settings.Default.SkipSettings)
            {
                if (!EnvSetup.ShowSetup())
                {
                    ErrorBox.ShowError("Unable to run application without those settings.", false);
                    Application.Exit();
                    return;
                }
            }

            // setup edition environment
            if (!SetupEnvironment())
            {
                // make sure the settings dialog is shown next time (Even though user disabled it)
                Properties.Settings.Default.SkipSettings = false;
                Properties.Settings.Default.Save();
                
                ErrorBox.ShowError("Failed to setup application environment. Unable to continue.", false);
                Application.Exit();
                return;
            }

            _DataEditors = new DataEditors.DataEditors(tabDataEditors, _Env);
            _DataEditors.OnObjectChanged += _DataEditors_OnObjectNameChanged;
            _DataEditors.OnEditorChanged += _DataEditors_OnEditorChanged;
        }

        void _DataEditors_OnEditorChanged(object sender)
        {
            RefreshUndoRedo();
        }

        void _DataEditors_OnObjectNameChanged(object sender, DataEditors.ObjectChangeEventArgs e)
        {
            // fresh object names in all data tabs
            try
            {
                foreach (TabPage tab in tabControl2.TabPages)
                {
                    var tree = (Widgets.DataObjectTree)tab.Controls[0];
                    tree.RefreshNames();
                }
            }
            catch (Exception)
            {}

            // refresh undo/redo UI
            RefreshUndoRedo();
        }

        private bool ChangeSettings()
        {
            // remember old settings
            var oldReflectionPath = Properties.Settings.Default.ReflectionPath;
            var oldDataPath = Properties.Settings.Default.DataPath;
            
            // allow user to change them
            if (!EnvSetup.ShowSetup())
                return false;

            // reload environment if paths changed
            if (Properties.Settings.Default.DataPath != oldDataPath || Properties.Settings.Default.ReflectionPath != oldReflectionPath)
            {
                // save everything, user may decide to cancel the change
                if (SaveChangedFiles(false, false) == DialogResult.Cancel)
                {
                    Properties.Settings.Default.ReflectionPath = oldReflectionPath;
                    Properties.Settings.Default.DataPath = oldDataPath;
                    Properties.Settings.Default.Save();
                    return false;
                }

                // reload the environment, may fail
                if (!SetupEnvironment())
                {
                    // revert settings
                    Properties.Settings.Default.ReflectionPath = oldReflectionPath;
                    Properties.Settings.Default.DataPath = oldDataPath;
                    Properties.Settings.Default.Save();

                    // restart
                    ErrorBox.ShowError("Failed to setup new environment.", false);

                    // restore
                    if (!SetupEnvironment())
                    {
                        ErrorBox.ShowError("Unable to restore previous environment. Exitting app.", false);
                        Application.Exit();
                    }

                    return false;
                }
            }

            // stored
            return true;
        }

        private Widgets.DataObjectTree OpenDataObjectTree(Red.GameData.Raw.ReflectionClass cls)
        {
            if (cls == null)
                return null;

            // get the actual root class
            var rootClass = cls.RootKeyClass;
            if (rootClass == null)
            {
                ErrorBox.ShowError("Class '" + cls.Name + "' is nameless (objects don't have a KEY property set). Unable to list them.");
                return null;
            }

            // select existing object list
            foreach (TabPage tab in tabControl2.TabPages)
            {
                try
                {
                    var classInterface = tab.Controls[0] as Widgets.DataObjectTree;
                    if (classInterface.RootClass == rootClass)
                    {
                        tabControl2.SelectedTab = tab;
                        return classInterface;
                    }
                }
                catch (Exception) { }
            }

            // create new data object list
            var dataTree = new Widgets.DataObjectTree(_Env, rootClass);
            dataTree.Dock = DockStyle.Fill;

            TabPage page = new TabPage("Objects '" + rootClass.Name + "'");
            page.Controls.Add(dataTree);

            dataTree.OnObjectActivated += dataTree_OnObjectActivated;
            dataTree.OnObjectDeleted += dataTree_OnObjectDeleted;

            tabControl2.TabPages.Add(page);
            tabControl2.SelectedTab = page;

            return dataTree;
        }

        void dataTree_OnObjectDeleted(object sender, DataObjectEventArgs e)
        {
            _DataEditors.CloseEditorForObject(e.ObjectView.RawData);
        }

        void dataTree_OnObjectNameChanged(object sender, Widgets.DataObjectEventArgs e)
        {
            _DataEditors.RefreshNames();
        }

        public class LameLogReporter : Red.GameData.Raw.IGenericFileErrorReporter
        {
            public string _ErrorMessage;

            public void StartFile(string filePath)
            {

            }

            public void Error(string error)
            {
                _ErrorMessage += error + "\r\n";
            }

            public void TotalProgress(int current, int total)
            {

            }

            public void FileProggres(int current, int total)
            {

            }
        }

        private bool SetupEnvironment()
        {  
            // load data
            var reflectionPath = Properties.Settings.Default.ReflectionPath;
            var dataPath = Properties.Settings.Default.DataPath;
            var log = new LameLogReporter();
            var env = Red.GameData.EditorEnvironment.Setup( dataPath, reflectionPath, log );
            if ( env == null )
            {
                ErrorBox.ShowError("Failed to setup environment:\r\n" + log._ErrorMessage, false);
                return false;
            }
         
            // close all editors
            if (_DataEditors != null )
                _DataEditors.CloseAllEditors();

            // close all class tabs
            tabControl2.TabPages.Clear();

            // change environment
            _Env = env;

            // setup class tree
            _ClassTree.BindReflection(_Env.Reflection);
            return true;
        }

        private void tabControl2_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Middle)
            {
                //Looping through the controls.
                for (int i = 0; i < this.tabControl2.TabPages.Count; i++)
                {
                    Rectangle r = tabControl2.GetTabRect(i);
                    if (r.Contains(e.Location))
                    {
                        //if (MessageBoxEx.Show(this, "Would you like to Close this Tab?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
                        {
                            tabControl2.TabPages.RemoveAt(i);
                            break;
                        }
                    }
                }
            }
        }

        private void saveChangesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if ( _Env == null )
            {
                ErrorBox.ShowError( "No data loaded so no data can be saved", false );
                return;
            }

            // save changes, don't ask the initial redundant question
            SaveChangedFiles(/*ask*/false, /*forceAllFiles*/false);
        }


        private void resaveEverythingToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_Env == null)
            {
                ErrorBox.ShowError("No data loaded so no data can be saved", false);
                return;
            }

            // force save all of the files
            SaveChangedFiles(/*ask*/false, /*forceAllFiles*/true);
        }

        private DialogResult SaveChangedFiles(bool ask, bool forceAllFiles)
        {
            // ask use if there's a will to save those files anyway
            if (ask)
            {
                SaveList dlg = new SaveList();
                var ret = dlg.SaveChanges(_Env);

                // report status
                if (ret != DialogResult.Yes)
                    return ret;
            }

            // cleanup object data
            _Env.Data.CleanupUnreachableObjects(_Env.Reflection);

            // assign unassigned objects to files
            {
                SaveAssignObjects dlg = new SaveAssignObjects();

                var rootDataPath = Properties.Settings.Default.DataPath;
                if (!dlg.AssignObjectsToFiles(_Env, rootDataPath))
                    return DialogResult.Cancel;
            }

            // save all files
            var log = new LameLogReporter();
            foreach (var file in _Env.Data.AllFiles)
            {
                try
                {
                    file.Save(log, forceAllFiles);
                }
                catch (Exception e)
                {
                    ErrorBox.ShowError("Error saving file '" + file.Path + "':\r\n" + e.ToString(), false );
                    return DialogResult.Cancel; // prevent aplication from closing
                }
            }

            // all went fine
            return DialogResult.OK;
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (e.CloseReason == CloseReason.UserClosing)
            {
                var ret = SaveChangedFiles(/*ask*/ true, /*forceAllFiles*/ false);

                // go back
                if (ret == DialogResult.Cancel)
                {
                    e.Cancel = true;
                    return;
                }
            }
        }

        private static bool IsFileReadOnly(string FileName)
        {
            try
            {
                FileInfo fInfo = new FileInfo(FileName);
                return fInfo.IsReadOnly && fInfo.Exists;
            }
            catch (Exception)
            {
                return false;
            }
        }

        private Builder _builder;

        private void verifyAndBuildF5ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // we need working path settings
            if (Properties.Settings.Default.CookerPath == "" || Properties.Settings.Default.OutputPath == "")
            {
                // ask user
                if (!ChangeSettings())
                    return;

                // still no settings :(
                if (Properties.Settings.Default.CookerPath == "" || Properties.Settings.Default.OutputPath == "")
                {
                    ErrorBox.ShowError("Cooker path and output path need to be specified to the building to happen");
                    return;
                }
            }

            // make sure target path is NOT read only
            if (IsFileReadOnly(Properties.Settings.Default.OutputPath))
            {
                ErrorBox.ShowError("Cooker output file is read only. Please remove the flag manually (No P4 integration)");
                return;
            }

            // show the builder
            if (_builder == null)
            {
                _builder = new Builder();
            }

            // create the build window
            var outputPath = Properties.Settings.Default.OutputPath;
            var cookerPath = Properties.Settings.Default.CookerPath;
            var dataPath = Properties.Settings.Default.DataPath;
            var reflectionPath = Properties.Settings.Default.ReflectionPath;
            _builder.Show();
            _builder.Focus();
            _builder.Start(cookerPath, outputPath, reflectionPath, dataPath, true);
        }

        private void RefreshUndoRedo()
        {
            var actionHistory = (_DataEditors != null) ? _DataEditors.ActionHistory : null;

            // update undo action
            if (actionHistory != null && actionHistory.HasUndo)
            {
                toolStripUndo.Enabled = true;
                undoToolStripMenuItem.Enabled = true;
                undoToolStripMenuItem.Text = "Undo: " + actionHistory.UndoActionDisplayName;
            }
            else
            {
                toolStripUndo.Enabled = false;
                undoToolStripMenuItem.Enabled = false;
                undoToolStripMenuItem.Text = "Undo";
            }

            // update redo action
            if (actionHistory != null && actionHistory.HasRedo)
            {
                toolStripRedo.Enabled = true;
                redoToolStripMenuItem.Enabled = true;
                redoToolStripMenuItem.Text = "Redo: " + actionHistory.RedoActionDisplayName;
            }
            else
            {
                toolStripRedo.Enabled = false;
                redoToolStripMenuItem.Enabled = false;
                redoToolStripMenuItem.Text = "Redo";
            }

            // redraw toolbar
            toolStrip1.Refresh();
        }

        private void undoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var actionHistory = _DataEditors.ActionHistory;
            if (actionHistory != null)
            {
                string errorMessage = "";
                if (!actionHistory.Undo(ref errorMessage))
                {
                    ErrorBox.ShowError("Undo error:\r\n" + errorMessage);
                }
            }

            RefreshUndoRedo();
        }

        private void redoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var actionHistory = _DataEditors.ActionHistory;
            if (actionHistory != null)
            {
                string errorMessage = "";
                if (!actionHistory.Redo(ref errorMessage))
                {
                    ErrorBox.ShowError("Redo error:\r\n" + errorMessage);
                }
            }

            RefreshUndoRedo();
        }
   

    }
}
